import React, { useEffect, useMemo, useRef } from 'react';
import { useMeasure } from 'react-use';
import { Table } from 'antd';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import bigDecimal from 'js-big-decimal';

import SummaryFoot from './SummaryFoot';
import styles from './index.less';

const isSafari = /safari/i.test(navigator.userAgent);

const matches =
    window.document.documentElement.matches ||
    window.document.documentElement.webkitMatchesSelector ||
    window.document.documentElement.mozMatchesSelector ||
    window.document.documentElement.oMatchesSelector ||
    window.document.documentElement.msMatchesSelector;

function isPlainObject(obj) {
    const prototype = Object.getPrototypeOf(obj);

    return (
        Object.prototype.toString.call(obj) === '[object Object]' &&
        (prototype === null || prototype === Object.getPrototypeOf({}))
    );
}

/**
 * 将给定的数组转成key，value为函数的对象
 * @example
 * covert2SummaryObj([
 *  'k1',
 *  { k2: (total, fieldValue) => (1 + total + fieldValue) },
 *  { k3: '汇总' }
 * ])
 * =>
 * {
 *  { 'k1': (total, fieldValue) => (total + fieldValue) },
 *  { k2: (total, fieldValue, row) => (row['a] + total + fieldValue) },
 *  { k3: () => '汇总' }
 * }
 * @param {*} summaryFieldList
 */
const covert2SummaryObj = summaryFieldList => {
    if (summaryFieldList == null) {
        return summaryFieldList;
    }

    const result = {};
    let fieldName = null;
    const defaultAccumulator = (total, current) => {
        if (current !== null && current !== undefined) {
            return bigDecimal.add(total, current);
        }
        return total;
    };
    summaryFieldList.forEach(summaryField => {
        if (typeof summaryField === 'string') {
            fieldName = summaryField;
            result[fieldName] = defaultAccumulator;
        } else if (isPlainObject(summaryField)) {
            const summaryFiledArray = Object.entries(summaryField)[0];
            // eslint-disable-next-line prefer-destructuring
            fieldName = summaryFiledArray[0];
            // eslint-disable-next-line prefer-destructuring
            const accumulator = summaryFiledArray[1];

            switch (typeof accumulator) {
                case 'string':
                    result[fieldName] = () => accumulator;
                    break;

                case 'function':
                    result[fieldName] = accumulator;
                    break;

                default:
                    result[fieldName] = () => '';
                    break;
            }
        } else {
            throw new Error(`汇总字段类型有误${JSON.stringify(summaryField)}`);
        }
    });
    return result;
};

const getScrollWidth = element => (element ? element.offsetWidth - element.clientWidth : 0);

/**
 * 给没有设置宽度的列添加默认的150的宽度
 * @param {*} columns 列项
 * @param tableElement
 */
const addDefaultWidth = (columns = [], tableElement) => {
    const tableWidth = tableElement ? tableElement.clientWidth : 0;
    const defaultWidth = 150;

    const addToLeaf = (columnList, extendInfo = {}) =>
        columnList.map(column => {
            const extraInfo = { ellipsis: true, ...extendInfo };

            if (!column.children || column.children.length === 0) {
                if (!column.width) {
                    extraInfo.width = defaultWidth;
                } else if (typeof column.width === 'string' && column.width.endsWith('%')) {
                    extraInfo.width = parseInt(String((tableWidth * parseFloat(column.width)) / 100), 10);
                }
                extraInfo.orgWidth = column.width;
            } else {
                extraInfo.children = addToLeaf(column.children, column.fixed ? { fixed: column.fixed } : null);
            }

            // 给表格头加鼠标移上去显示全部title属性
            if (column && typeof column.title === 'string') {
                extraInfo.title = <span title={column.title}>{column.title}</span>;
            }

            return { ...column, ...extraInfo };
        });

    return addToLeaf(columns);
};

/**
 * 计算表格宽度之后
 * @param columns
 * @returns {*}
 */
const calTotalWidth = columns => {
    const cal = columnList =>
        columnList.reduce((total, column) => {
            let result = total;

            if (!column.children || column.children.length === 0) {
                result = bigDecimal.add(result, column.width || 0);
            } else {
                result = bigDecimal.add(result, cal(column.children));
            }

            return result;
        }, 0);
    return cal(columns);
};

/**
 * 计算固定列的宽度
 * @param columns
 * @returns {*}
 */
const calSumFixedWidth = columns => {
    const cal = (columnList, accumulator) =>
        columnList.reduce((totalSum, column) => {
            let result = totalSum;

            if (!column.children || column.children.length === 0) {
                if (column.fixed === true || column.fixed === 'left') {
                    result.left = bigDecimal.add(result.left, column.width || 0);
                }

                if (column.fixed === 'right') {
                    result.right = bigDecimal.add(result.right, column.width || 0);
                }
            } else {
                result = cal(column.children, totalSum);
            }

            return result;
        }, accumulator);

    return cal(columns, { left: 0, right: 0 });
};

/**
 * 按比例将每列伸缩到表格不出现水平滚动条
 * @param columns 列定义
 * @param sumInfo 表格计算汇总信息
 * @param tableWidth 表格宽度
 * @param vvForceToFit 是否强制按比例缩放
 * @returns {*}
 */
const shrinkToFit = (columns = [], vvForceToFit, sumInfo, tableWidth) => {
    if (sumInfo.sum > tableWidth && !vvForceToFit) {
        // eslint-disable-next-line consistent-return
        return columns;
    }
    // table去掉固定Fixed的宽度和
    const tableExcludeFixedWidth = tableWidth - sumInfo.fixedSum;

    // 原有剩余的宽度和
    const colSumExcludeFixedWidth = sumInfo.sum - sumInfo.fixedSum;

    //
    let columnNoFixedSumWidth = 0;

    // 重新给每列计算值
    const shrinkColumn = columnList =>
        columnList.map(column => {
            const result = { ...column };

            if (column.children && column.children.length > 0) {
                result.children = shrinkColumn(column.children);
            } else if (result.dataIndex === sumInfo.lastColumnNoFixed?.dataIndex) {
                result.width = tableExcludeFixedWidth - columnNoFixedSumWidth;
            } else {
                result.width = column.fixed
                    ? column.width
                    : parseInt(String((column.width * tableExcludeFixedWidth) / colSumExcludeFixedWidth), 10);

                if (!column.fixed) {
                    columnNoFixedSumWidth += result.width;
                }
            }
            return result;
        });

    // console.log('scrollWidth:', getScrollWidth(tableElement));
    // console.log('tableWidth:', tableWidth);
    // console.log('before shrink:', columns);
    // console.log('after shrink:', shrinkColumn(columns));
    // eslint-disable-next-line consistent-return
    return shrinkColumn(columns);
};

const setWidthForSummaryFoot = (refBox, leftWidth, rightWidth) => {
    const task = setInterval(() => {
        try {
            const footer = refBox.querySelector('.ant-table-footer');
            const leftFixed = footer.querySelector('.ant-table-fixed-left .ant-table-fixed');
            const leftOuterFixed = footer.querySelector('.ant-table-fixed-left .ant-table-body-outer');
            const leftInnerFixed = footer.querySelector('.ant-table-fixed-left .ant-table-body-inner');

            const rightFixed = footer.querySelector('.ant-table-fixed-right .ant-table-fixed');

            if (leftFixed || rightFixed) {
                if (leftFixed) {
                    leftFixed.style.width = `${leftWidth}px`;
                }
                if (leftOuterFixed && leftInnerFixed) {
                    leftOuterFixed.style.width = `${leftWidth}px`;
                    leftInnerFixed.style.width = `${leftWidth}px`;
                }
                if (rightFixed) {
                    rightFixed.style.width = `${rightWidth}px`;
                }
                clearInterval(task);
            }
        } catch {
            clearInterval(task);
        }
    }, 50);
};

const SummaryTable = ({ columns, dataSource, vvSummaryFields, vvShrinkToFit, vvForceToFit, ...otherTableProps }) => {
    const refBox = useRef(null);
    const [ref, { width }] = useMeasure();
    const clazzNames = classNames(styles.summaryTable, otherTableProps.className);

    const summaryObj = covert2SummaryObj(vvSummaryFields);
    // 获得汇总数据
    const footData = useMemo(
        () =>
            summaryObj == null
                ? null
                : dataSource.reduce(
                      (total, row) => {
                          Object.entries(summaryObj).forEach(([fieldName, valueHandle]) => {
                              total[fieldName] = valueHandle(total[fieldName], row[fieldName], row);
                          });
                          return total;
                      },
                      { $$isSummary: true }
                  ),
        [dataSource, summaryObj]
    );
    const footDataSource = footData ? [footData] : [];
    // console.log('footDataSource:', footDataSource);
    useEffect(() => {
        // 没有固定高度时，用ant-table-content
        // 有固定高度时，用ant-table-body
        const tableBody =
            refBox.current.querySelector('.ant-table-body') || refBox.current.querySelector('.ant-table-content');

        const footBody =
            refBox.current.querySelector('.ant-table-footer .ant-table-body') ||
            refBox.current.querySelector('.ant-table-footer .ant-table-content');

        if (footBody) {
            // 计算滚动条的宽度
            const scrollWidth = getScrollWidth(tableBody);
            footBody.style.marginRight = `${scrollWidth}px`;

            const scrollFn = () => {
                if (tableBody.scrollLeft !== footBody.scrollLeft) {
                    footBody.scrollLeft = tableBody.scrollLeft;
                }
            };
            tableBody.addEventListener('scroll', scrollFn);

            return () => {
                tableBody.removeEventListener('scroll', scrollFn);
            };
        }

        return undefined;
    });

    const calInfo = useMemo(() => {
        const sumFun = (colList, initialTotal) =>
            colList.reduce((total, column) => {
                const result = { ...total };
                if (column.children && column.children.length > 0) {
                    const childrenInfo = sumFun(column.children, {
                        sum: 0,
                        fixedSum: 0
                    });
                    result.sum += childrenInfo.sum;
                    result.fixedSum += childrenInfo.fixedSum;
                    result.lastColumnNoFixed = childrenInfo.lastColumnNoFixed
                        ? childrenInfo.lastColumnNoFixed
                        : total.lastColumnNoFixed;
                } else if (!column.children || column.children.length === 0) {
                    result.sum += column.width;
                    result.fixedSum += column.fixed ? column.width : 0;
                    if (!column.fixed) {
                        result.lastColumnNoFixed = column;
                    }
                }
                return result;
            }, initialTotal);

        const tableElement = refBox.current;
        const scrollWidth = getScrollWidth(refBox.current);
        // 因为border多出1px会导致拉满后出现滚动条
        const tableWidth = tableElement ? tableElement.clientWidth - scrollWidth - 1 : 1;

        const wColumns = addDefaultWidth(columns, refBox.current);
        const sumInfo = sumFun(wColumns, {
            // 所有列的合计值
            sum: 0,
            // 所有固定列的合计值
            fixedSum: 0,
            lastColumnNoFixed: null
        });
        const wrappedColumns = tableWidth > 5 ? shrinkToFit(wColumns, vvForceToFit, sumInfo, tableWidth) : wColumns;

        const scrollX = sumInfo.sum < tableWidth ? {} : { x: +calTotalWidth(wrappedColumns) };
        let scroll;
        if (otherTableProps.scroll && isPlainObject(otherTableProps.scroll)) {
            scroll = { ...otherTableProps.scroll, ...scrollX };
        } else if (otherTableProps.scroll) {
            scroll = Object.assign(otherTableProps.scroll, ...scrollX);
        } else {
            scroll = scrollX;
        }

        return {
            wrappedColumns,
            scroll
        };
    }, [columns, vvShrinkToFit, refBox, width]);

    useEffect(() => {
        // 没有固定高度时，用ant-table-content
        // 有固定高度时，用ant-table-body
        const tableBody =
            refBox.current.querySelector('.ant-table-body') || refBox.current.querySelector('.ant-table-content');
        const tableContent = tableBody.parentNode.parentNode;
        const childNodesArray = Array.from(tableContent.childNodes);
        const leftFixed = childNodesArray
            .find(node => matches.call(node, '.ant-table-fixed-left'))
            ?.querySelector('.ant-table-fixed');
        const rightFixed = childNodesArray
            .find(node => matches.call(node, '.ant-table-fixed-right'))
            ?.querySelector('.ant-table-fixed');

        if (leftFixed || rightFixed) {
            const fixedSumInfo = calSumFixedWidth(calInfo.wrappedColumns);
            let leftWidth = fixedSumInfo.left;

            // 若配置了勾选框
            if (otherTableProps?.rowSelection?.fixed) {
                leftWidth = bigDecimal.add(leftWidth, otherTableProps?.rowSelection?.columnWidth || 60);
                leftWidth = bigDecimal.add(leftWidth, isSafari ? 1 : 0);
            }
            if (leftFixed) {
                leftFixed.style.width = `${leftWidth}px`;
            }
            if (rightFixed) {
                rightFixed.style.width = `${fixedSumInfo.right}px`;
            }

            setWidthForSummaryFoot(refBox.current, leftWidth, fixedSumInfo.right);
        }
    }, [calInfo.wrappedColumns]);

    return (
        <div ref={ref}>
            <div ref={refBox}>
                <Table
                    {...otherTableProps}
                    columns={calInfo.wrappedColumns}
                    dataSource={dataSource}
                    footer={() =>
                        dataSource && dataSource.length > 0 && vvSummaryFields.length !== 0 ? (
                            <SummaryFoot
                                {...otherTableProps}
                                rowKey="$$isSummary"
                                columns={calInfo.wrappedColumns}
                                dataSource={footDataSource}
                                scroll={calInfo.scroll}
                            />
                        ) : null
                    }
                    className={clazzNames}
                    scroll={calInfo.scroll}
                    tableLayout="fixed"
                />
            </div>
        </div>
    );
};

SummaryTable.propTypes = {
    columns: PropTypes.array,
    dataSource: PropTypes.array,
    vvSummaryFields: PropTypes.array,
    vvShrinkToFit: PropTypes.bool,
    vvForceToFit: PropTypes.bool
};

SummaryTable.defaultProps = {
    columns: [],
    dataSource: [],
    // 汇总字段
    vvSummaryFields: [],
    // 列（column.width）宽度之和时小于表格宽度是否要按比例缩放,vvShrinkToFit:false的情况待开发
    vvShrinkToFit: true,
    // 强制按比例缩放保证不出现滚动条
    vvForceToFit: false
};

export default React.memo(SummaryTable);
